#ifdef WX_PRECOMP
#include "wx_pch.h"
#else
#include <wx/button.h>
#include <wx/imaglist.h>
#include <wx/intl.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>
#include <wx/menu.h>
#include <wx/colour.h>
#include <wx/filedlg.h>
#include <wx/tokenzr.h>
#endif
#include <wx/filename.h>
#include <wx/toplevel.h>
#include <wx/tooltip.h>


#include <wx/clipbrd.h>

#ifdef __BORLANDC__
#pragma hdrstop
#endif


#include "sdk.h"
#ifndef CB_PRECOMP
#include "manager.h"
#include "logmanager.h"
#include "macrosmanager.h"
#include "configmanager.h"
#include "editormanager.h"
#include "wx/wxscintilla.h"
#include "cbeditor.h"
#include "globals.h"
#endif
#include "cbstyledtextctrl.h"

#include "version.h"
#include "codesnippetswindow.h"
#include "snippetitemdata.h"
#include <tinyxml.h>
#include "snippetsconfig.h"
#include "snippetsimages.h"
#include "codesnippetstreectrl.h"
#include "GenericMessageBox.h"
#include "settingsdlg.h"
#include "menuidentifiers.h"


int idSearchSnippetCtrl         = wxNewId();
int idSearchCfgBtn              = wxNewId();
int idSnippetsTreeCtrl          = wxNewId();


int idMnuAddSubCategory         = wxNewId();
int idMnuRemove                 = wxNewId();
int idMnuRename                 = wxNewId();
int idMnuCopy                   = wxNewId();
int idMnuPaste                  = wxNewId();
int idMnuConvertToCategory      = wxNewId();
int idMnuAddSnippet             = wxNewId();
int idMnuApplySnippet           = wxNewId();
int idMnuLoadSnippetsFromFile   = wxNewId();
int idMnuSaveSnippets           = wxNewId();
int idMnuSaveSnippetsToFile     = wxNewId();
int idMnuFileBackup             = wxNewId();
int idMnuRemoveAll              = wxNewId();
int idMnuCopyToClipboard        = wxNewId();
int idMnuEditSnippet            = wxNewId();
int idMnuOpenFileLink           = wxNewId();
int idMnuConvertToFileLink      = wxNewId();
int idMnuProperties             = wxNewId();
int idMnuSettings               = wxNewId();
int idMnuAbout                  = wxNewId();
int idMnuTest                   = wxNewId();


int idMnuCaseSensitive          = wxNewId();
int idMnuClear                  = wxNewId();
int idMnuScope                  = wxNewId();
int idMnuScopeSnippets          = wxNewId();
int idMnuScopeCategories        = wxNewId();
int idMnuScopeBoth              = wxNewId();
int idMnuSearchExtended         = wxNewId();


BEGIN_EVENT_TABLE(CodeSnippetsWindow, wxPanel)


  EVT_MENU(idMnuRemove,           CodeSnippetsWindow::OnMnuRemove)
  EVT_MENU(idMnuRename,           CodeSnippetsWindow::OnMnuRename)
  EVT_MENU(idMnuCopy,             CodeSnippetsWindow::OnMnuCopy)
  EVT_MENU(idMnuPaste,            CodeSnippetsWindow::OnMnuPaste)
  EVT_MENU(idMnuConvertToCategory, CodeSnippetsWindow::OnMnuConvertToCategory)
  EVT_MENU(idMnuAddSubCategory,   CodeSnippetsWindow::OnMnuAddSubCategory)
  EVT_MENU(idMnuAddSnippet,       CodeSnippetsWindow::OnMnuAddSnippet)
  EVT_MENU(idMnuApplySnippet,     CodeSnippetsWindow::OnMnuApplySnippet)
  EVT_MENU(idMnuLoadSnippetsFromFile, CodeSnippetsWindow::OnMnuLoadSnippetsFromFile)
  EVT_MENU(idMnuSaveSnippets,     CodeSnippetsWindow::OnMnuSaveSnippets)
  EVT_MENU(idMnuSaveSnippetsToFile, CodeSnippetsWindow::OnMnuSaveSnippetsAs)
  EVT_MENU(idMnuFileBackup,       CodeSnippetsWindow::OnMnuFileBackup)
  EVT_MENU(idMnuRemoveAll,        CodeSnippetsWindow::OnMnuRemoveAll)
  EVT_MENU(idMnuCopyToClipboard,  CodeSnippetsWindow::OnMnuCopyToClipboard)
  EVT_MENU(idMnuEditSnippet,      CodeSnippetsWindow::OnMnuEditSnippet)
  EVT_MENU(idMnuOpenFileLink,     CodeSnippetsWindow::OnMnuOpenFileLink)
  EVT_MENU(idMnuConvertToFileLink, CodeSnippetsWindow::OnMnuSaveSnippetAsFileLink)
  EVT_MENU(idMnuProperties,       CodeSnippetsWindow::OnMnuProperties)
  EVT_MENU(idMnuSettings,         CodeSnippetsWindow::OnMnuSettings)
  EVT_MENU(idMnuAbout,            CodeSnippetsWindow::OnMnuAbout)
  EVT_MENU(idMnuTest,             CodeSnippetsWindow::OnMnuTest)



  EVT_MENU(idMnuCaseSensitive, CodeSnippetsWindow::OnMnuCaseSensitive)
  EVT_MENU(idMnuScopeSnippets, CodeSnippetsWindow::OnMnuChangeScope)
  EVT_MENU(idMnuScopeCategories, CodeSnippetsWindow::OnMnuChangeScope)
  EVT_MENU(idMnuScopeBoth, CodeSnippetsWindow::OnMnuChangeScope)
  EVT_MENU(idMnuClear, CodeSnippetsWindow::OnMnuClear)
  EVT_MENU(idMnuSearchExtended, CodeSnippetsWindow::OnMnuSearchExtended)


  EVT_BUTTON(idSearchCfgBtn, CodeSnippetsWindow::OnSearchCfg)
  EVT_TEXT(idSearchSnippetCtrl, CodeSnippetsWindow::OnSearch)
  EVT_TREE_ITEM_ACTIVATED(idSnippetsTreeCtrl, CodeSnippetsWindow::OnItemActivated)
  EVT_TREE_ITEM_MENU(idSnippetsTreeCtrl, CodeSnippetsWindow::OnItemMenu)
  EVT_TREE_BEGIN_DRAG(idSnippetsTreeCtrl, CodeSnippetsWindow::OnBeginDrag)
  EVT_TREE_END_DRAG(idSnippetsTreeCtrl, CodeSnippetsWindow::OnEndDrag)
  EVT_TREE_BEGIN_LABEL_EDIT(idSnippetsTreeCtrl, CodeSnippetsWindow::OnBeginLabelEdit)
  EVT_TREE_END_LABEL_EDIT(idSnippetsTreeCtrl, CodeSnippetsWindow::OnEndLabelEdit)
  EVT_TREE_ITEM_GETTOOLTIP(idSnippetsTreeCtrl, CodeSnippetsWindow::OnItemGetToolTip)


  EVT_CLOSE(CodeSnippetsWindow::OnClose)

  EVT_LEAVE_WINDOW(CodeSnippetsWindow::OnLeaveWindow)
  EVT_ENTER_WINDOW(CodeSnippetsWindow::OnEnterWindow)

END_EVENT_TABLE()


bool SnippetsDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString& data) {
  m_TreeCtrl->SetFocus();
  wxPoint p(x, y);
  int hitTestFlags = 0;
  wxTreeItemId itemID = m_TreeCtrl->HitTest(p, hitTestFlags);
  if((hitTestFlags) & (wxTREE_HITTEST_ONITEMBUTTON | wxTREE_HITTEST_ONITEMICON | wxTREE_HITTEST_ONITEMLABEL)) {
    if(SnippetTreeItemData* item = (SnippetTreeItemData*)(m_TreeCtrl->GetItemData(itemID))) {
      switch(item->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          m_TreeCtrl->AddCodeSnippet(m_TreeCtrl->GetRootItem(), _("New snippet"), data, 0, true);
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          m_TreeCtrl->AddCodeSnippet(item->GetId(), _("New snippet"), data, 0, true);
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          item->SetSnippetString(data);
          break;
      }
      m_TreeCtrl->SetFileChanged(true);
      return true;
    } else
      return false;
  } else
    return false;
}


CodeSnippetsWindow::CodeSnippetsWindow(wxWindow* parent)

  : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, wxT("csPanel")) {
  m_SnippetsTreeCtrl = 0;
  m_SearchSnippetCtrl = 0;
  m_SearchCfgBtn = 0;
  m_isCheckingForExternallyModifiedFiles = false;
  pTiXmlDoc = 0;
  m_bIsEditingLabel = false;
  if(not GetConfig()->pMainFrame)
    GetConfig()->pMainFrame = parent;
  GetConfig()->pSnippetsWindow = this;
  InitDlg();
  m_AppendItemsFromFile = false;
  GetConfig()->SettingsLoad();
  wxString s(__FUNCTION__, wxConvUTF8);
  LOGIT(s + wxT("LoadingFile:%s"), GetConfig()->SettingsSnippetsXmlPath.c_str());
  GetSnippetsTreeCtrl()->LoadItemsFromFile(GetConfig()->SettingsSnippetsXmlPath, false);
}

CodeSnippetsWindow::~CodeSnippetsWindow() {
  #if defined(LOGGING)
  #endif
}

void CodeSnippetsWindow::OnEnterWindow(wxMouseEvent &event) {
  #if defined(LOGGING)
  #endif
  event.Skip();
}

void CodeSnippetsWindow::OnLeaveWindow(wxMouseEvent &event) {
  #if defined(LOGGING)
  #endif
  event.Skip();
}

void CodeSnippetsWindow::OnClose(wxCloseEvent& event) {
  if(GetConfig()->m_appIsShutdown) {
    event.Skip();
    return;
  }
  if(not GetConfig()->GetSnippetsWindow())
  {event.Skip(); return;}
  #if defined(LOGGING)
  LOGIT(_T("CodeSnippetsWindow::Onclose Saving Settings"));
  #endif
  GetConfig()->SettingsSave();
  if(GetConfig()->IsPlugin()) {
    if(GetConfig()->IsFloatingWindow())
      GetConfig()->SettingsSaveWinPosition();
  }
  if((not GetConfig()->m_appIsShutdown) && (not GetConfig()->m_appIsDisabled)) {
    wxMenuBar* pbar = GetConfig()->m_pMenuBar;
    pbar->Check(idViewSnippets, false);
  }
  if(not GetConfig()->IsPlugin()) {
    Destroy();
    GetConfig()->pSnippetsWindow = 0;
  }
  event.Skip();
  #if defined(LOGGING)
  LOGIT(_T("CodeSnippetsWindow::OnClose END"));
  #endif
}

void CodeSnippetsWindow::InitDlg() {
  wxColor maskColor(255, 0, 255);
  wxBoxSizer* parentSizer = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* searchCtrlSizer = new wxBoxSizer(wxHORIZONTAL);
  m_SearchSnippetCtrl = new wxTextCtrl(this, idSearchSnippetCtrl, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
  searchCtrlSizer->Add(m_SearchSnippetCtrl, 1, wxBOTTOM | wxLEFT | wxTOP, 5);
  m_SearchCfgBtn = new wxButton(this, idSearchCfgBtn, _T(">"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
  searchCtrlSizer->Add(m_SearchCfgBtn, 0, wxBOTTOM | wxRIGHT | wxTOP, 5);
  parentSizer->Add(searchCtrlSizer, 0, wxEXPAND, 5);
  wxBoxSizer* treeCtrlSizer = new wxBoxSizer(wxVERTICAL);
  m_SnippetsTreeCtrl = new CodeSnippetsTreeCtrl(this, idSnippetsTreeCtrl, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE | wxTR_EDIT_LABELS);
  treeCtrlSizer->Add(GetSnippetsTreeCtrl(), 1, wxEXPAND, 5);
  parentSizer->Add(treeCtrlSizer, 1, wxEXPAND, 5);
  SetSizer(parentSizer);
  Layout();
  GetSnippetsTreeCtrl()->SetDropTarget(new SnippetsDropTarget(GetSnippetsTreeCtrl()));
  GetSnippetsTreeCtrl()->SetImageList(GetSnipImageList());
  SnippetTreeItemData* rootData = new SnippetTreeItemData(SnippetTreeItemData::TYPE_ROOT);
  GetSnippetsTreeCtrl()->AddRoot(_("All snippets"), 0, -1, rootData);
  GetConfig()->pSnippetsSearchCtrl = m_SearchSnippetCtrl;
}

void CodeSnippetsWindow::OnSearchCfg(wxCommandEvent&) {
  wxMenu* searchCfgMenu = new wxMenu();
  wxMenu* searchScopeMenu = new wxMenu();
  searchScopeMenu->AppendRadioItem(idMnuScopeSnippets, _("Snippets"));
  searchScopeMenu->AppendRadioItem(idMnuScopeCategories, _("Categories"));
  searchScopeMenu->AppendRadioItem(idMnuScopeBoth, _("Snippets and categories"));
  switch(GetConfig()->m_SearchConfig.scope) {
    case CodeSnippetsConfig::SCOPE_SNIPPETS:
      searchScopeMenu->Check(idMnuScopeSnippets, true);
      break;
    case CodeSnippetsConfig::SCOPE_CATEGORIES:
      searchScopeMenu->Check(idMnuScopeCategories, true);
      break;
    case CodeSnippetsConfig::SCOPE_BOTH:
      searchScopeMenu->Check(idMnuScopeBoth, true);
      break;
  }
  searchCfgMenu->AppendCheckItem(idMnuCaseSensitive, _("Case sensitive"));
  if(GetConfig()->m_SearchConfig.caseSensitive)
    searchCfgMenu->Check(idMnuCaseSensitive, true);
  searchCfgMenu->Append(idMnuScope, _("Scope"), searchScopeMenu);
  searchCfgMenu->AppendSeparator();
  searchCfgMenu->Append(idMnuClear, _("Clear"));
  searchCfgMenu->Append(idMnuSettings, _("Settings..."));
  if(m_SearchSnippetCtrl->GetValue().IsEmpty())
    searchCfgMenu->Enable(idMnuClear, false);
  wxRect btnRect = m_SearchCfgBtn->GetRect();
  PopupMenu(searchCfgMenu, btnRect.x + btnRect.GetWidth(), btnRect.y);
  searchCfgMenu->Destroy(idMnuScope);
  delete searchCfgMenu;
}

void CodeSnippetsWindow::OnSearch(wxCommandEvent&) {
  if(m_SearchSnippetCtrl->GetValue().IsEmpty()) {
    GetSnippetsTreeCtrl()->SetItemText(GetSnippetsTreeCtrl()->GetRootItem(), _("All snippets"));
    m_SearchSnippetCtrl->SetBackgroundColour(wxNullColour);
    m_SearchSnippetCtrl->Refresh();
  } else {
    GetSnippetsTreeCtrl()->SetItemText(GetSnippetsTreeCtrl()->GetRootItem(), wxString::Format(_("Search \"%s\""), m_SearchSnippetCtrl->GetValue().c_str()));
    wxString searchTerms = m_SearchSnippetCtrl->GetValue();
    if(not GetConfig()->m_SearchConfig.caseSensitive)
      searchTerms.LowerCase();
    wxTreeItemId foundID = SearchSnippet(searchTerms, GetSnippetsTreeCtrl()->GetRootItem());
    if(foundID.IsOk()) {
      GetSnippetsTreeCtrl()->EnsureVisible(foundID);
      GetSnippetsTreeCtrl()->SelectItem(foundID);
      m_SearchSnippetCtrl->SetBackgroundColour(wxNullColour);
      m_SearchSnippetCtrl->Refresh();
    } else {
      GetSnippetsTreeCtrl()->EnsureVisible(GetSnippetsTreeCtrl()->GetRootItem());
      GetSnippetsTreeCtrl()->SelectItem(GetSnippetsTreeCtrl()->GetRootItem());
      m_SearchSnippetCtrl->SetBackgroundColour(wxColor(244, 168, 168));
      m_SearchSnippetCtrl->Refresh();
    }
  }
}

void CodeSnippetsWindow::OnItemActivated(wxTreeEvent& event) {
  if(IsTreeBusy())
    return;
  wxTreeCtrl* wx_tree = (wxTreeCtrl*)event.GetEventObject();
  if(not wx_tree)
    return;
  wxTreeItemId itemId = event.GetItem();
  SnippetTreeItemData* eventItem = (SnippetTreeItemData*)(wx_tree->GetItemData(itemId));
  SetAssociatedItemID(itemId);
  if(eventItem->GetType() == SnippetTreeItemData::TYPE_CATEGORY) {
    wxTreeCtrl* ptree = ((wxTreeCtrl*)event.GetEventObject());
    if(ptree->IsExpanded(itemId))
      ptree->Collapse(itemId);
    else
      ptree->Expand(itemId);
    return;
  }
  if(::wxGetKeyState(WXK_SHIFT)) {
    ApplySnippet(event.GetItem());
    return;
  }
  wxCommandEvent ev;
  if(::wxGetKeyState(WXK_ALT)) {
    OnMnuOpenFileLink(ev);
    return;
  }
  OnMnuEditSnippet(ev)        ;
  return;
}

void CodeSnippetsWindow::OnItemMenu(wxTreeEvent& event) {
  if(IsTreeBusy())
    return;
  wxTreeItemId itemId = event.GetItem();
  if(const SnippetTreeItemData* eventItem = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(event.GetItem()))) {
    wxMenu* snippetsTreeMenu = new wxMenu();
    switch(eventItem->GetType()) {
      case SnippetTreeItemData::TYPE_ROOT:
        snippetsTreeMenu->Append(idMnuAddSnippet, _("Add Snippet"));
        snippetsTreeMenu->Append(idMnuAddSubCategory, _("Add SubCategory"));
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuPaste, _("Paste Items"));
        snippetsTreeMenu->Enable(idMnuPaste, pTiXmlDoc);
        snippetsTreeMenu->Append(idMnuRemoveAll, _("Remove all items"));
        if(!GetSnippetsTreeCtrl()->ItemHasChildren(GetSnippetsTreeCtrl()->GetRootItem()))
          snippetsTreeMenu->Enable(idMnuRemoveAll, false);
        if(GetConfig()->IsPlugin()) {
          snippetsTreeMenu->AppendSeparator();
          snippetsTreeMenu->Append(idMnuSaveSnippets, _("Save Index"));
          snippetsTreeMenu->Append(idMnuSaveSnippetsToFile, _("Save Index As..."));
          snippetsTreeMenu->Append(idMnuFileBackup, _("Backup Main Index"));
          if(!GetSnippetsTreeCtrl()->ItemHasChildren(GetSnippetsTreeCtrl()->GetRootItem())) {
            snippetsTreeMenu->Enable(idMnuSaveSnippetsToFile, false);
            snippetsTreeMenu->Enable(idMnuSaveSnippets, false);
            snippetsTreeMenu->Enable(idMnuFileBackup, false);
          }
          if(::wxGetKeyState(WXK_SHIFT)) {
            snippetsTreeMenu->Append(idMnuLoadSnippetsFromFile, _("Load Index File (append)..."));
            m_AppendItemsFromFile = true;
          } else {
            snippetsTreeMenu->Append(idMnuLoadSnippetsFromFile, _("Load Index File..."));
            m_AppendItemsFromFile = false;
          }
        }
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuSettings, _("Settings..."));
        if(GetConfig()->IsPlugin()) {
          snippetsTreeMenu->Append(idMnuAbout, _("About..."));
          #if defined(LOGGING)
          snippetsTreeMenu->Append(idMnuTest, _("Test"));
          #endif
        }
        break;
      case SnippetTreeItemData::TYPE_CATEGORY:
        snippetsTreeMenu->Append(idMnuAddSnippet, _("Add Snippet"));
        snippetsTreeMenu->Append(idMnuAddSubCategory, _("Add SubCategory"));
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuCopy,  _("Copy  Category"));
        snippetsTreeMenu->Append(idMnuPaste, _("Paste Tree Items"));
        snippetsTreeMenu->Enable(idMnuPaste, pTiXmlDoc);
        snippetsTreeMenu->Append(idMnuRename, _("Rename"));
        snippetsTreeMenu->Append(idMnuRemove, _("Remove"));
        break;
      case SnippetTreeItemData::TYPE_SNIPPET:
        if(IsFileSnippet(itemId)) {
          snippetsTreeMenu->Append(idMnuEditSnippet, _("Edit File"));
          wxString fileExt(GetSnippetsTreeCtrl()->GetFileLinkExt(itemId));
          if(not fileExt.IsEmpty())
            fileExt = wxT(" (as ") + fileExt + wxT(")");
          snippetsTreeMenu->Append(idMnuOpenFileLink, _("Open File") + fileExt);
        } else {
          snippetsTreeMenu->Append(idMnuEditSnippet, _("Edit Text"));
          if(IsUrlSnippet())
            snippetsTreeMenu->Append(idMnuOpenFileLink, _("Open Url"));
        }
        if(GetConfig()->IsPlugin())
          snippetsTreeMenu->Append(idMnuApplySnippet, _("Append to active window"));
        if(IsFileSnippet(itemId))
          snippetsTreeMenu->Append(idMnuCopyToClipboard, _("Clipboard <= FileName"));
        else
          snippetsTreeMenu->Append(idMnuCopyToClipboard, _("Clipboard <= Text"));
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuConvertToCategory, _("Convert to Category"));
        snippetsTreeMenu->Append(idMnuConvertToFileLink, _("Convert to File Link..."));
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuCopy,  _("Copy Tree Item"));
        snippetsTreeMenu->Append(idMnuPaste, _("Paste Tree Item"));
        snippetsTreeMenu->Enable(idMnuPaste, pTiXmlDoc);
        snippetsTreeMenu->Append(idMnuRemove, _("Remove"));
        snippetsTreeMenu->AppendSeparator();
        snippetsTreeMenu->Append(idMnuProperties, _("Properties..."));
        break;
    }
    GetSnippetsTreeCtrl()->SetAssociatedItemID(eventItem->GetId());
    PopupMenu(snippetsTreeMenu);
    delete snippetsTreeMenu;
  }
}

void CodeSnippetsWindow::OnBeginDrag(wxTreeEvent&) {
}

void CodeSnippetsWindow::OnEndDrag(wxTreeEvent&) {
}

void CodeSnippetsWindow::OnMnuAddSubCategory(wxCommandEvent& event) {
  CodeSnippetsTreeCtrl* pTree = GetSnippetsTreeCtrl();
  wxTreeItemId newItem =
    GetSnippetsTreeCtrl()->AddCategory(GetSnippetsTreeCtrl()->GetAssociatedItemID(),
                                       _("New category"), 0, true);
  SetFileChanged(true);
  if(newItem.IsOk()) {
    pTree->SelectItem(newItem);
    pTree->SetAssociatedItemID(newItem);
    OnMnuRename(event);
  }
  if((newItem.IsOk()) && (pTree->GetItemText(newItem).IsEmpty()))
    pTree->RemoveItem(newItem);
}

void CodeSnippetsWindow::OnMnuRemove(wxCommandEvent&) {
  wxTreeItemId itemID = GetAssociatedItemID();
  GetSnippetsTreeCtrl()->RemoveItem(itemID);
}

void CodeSnippetsWindow::OnMnuRename(wxCommandEvent& event) {
  wxTreeItemId itemID = GetAssociatedItemID();
  CodeSnippetsTreeCtrl* pTree = GetSnippetsTreeCtrl();
  wxString itemLabel = pTree->GetItemText(itemID);
  wxPoint currentMousePosn = ::wxGetMousePosition();
  wxString newLabel = ::cbGetTextFromUser(wxT("New Category Label"), wxT("Rename"),
                                          itemLabel, pTree,
                                          currentMousePosn.x, currentMousePosn.y, false);
  LOGIT(_T("GetTextFromUser[%s] oldLabel[%s]"), newLabel.c_str(), itemLabel.c_str());
  if(not newLabel.IsEmpty())
    pTree->SetItemText(itemID, newLabel);
  if(itemID.IsOk() && pTree->GetItemText(itemID).IsEmpty())
    pTree->RemoveItem(itemID);
}

void CodeSnippetsWindow::OnMnuCopy(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  if(pTiXmlDoc) {
    delete pTiXmlDoc;
    pTiXmlDoc = 0;
  }
  pTiXmlDoc = GetSnippetsTreeCtrl()->CopyTreeNodeToXmlDoc(GetAssociatedItemID());
}

void CodeSnippetsWindow::OnMnuPaste(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  if(not pTiXmlDoc)
    return;
  wxTreeItemId targetItem = GetAssociatedItemID();
  if(IsSnippet(targetItem)) {
    if((targetItem = GetSnippetsTreeCtrl()->ConvertSnippetToCategory(targetItem)))
      ;
    else
      return;
  }
  GetSnippetsTreeCtrl()->CopyXmlDocToTreeNode(pTiXmlDoc, targetItem);
  if(pTiXmlDoc) {
    delete pTiXmlDoc;
    pTiXmlDoc = 0;
  }
}

void CodeSnippetsWindow::OnMnuConvertToCategory(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  GetSnippetsTreeCtrl()->ConvertSnippetToCategory(GetAssociatedItemID());
}

void CodeSnippetsWindow::OnMnuAddSnippet(wxCommandEvent&) {
  GetSnippetsTreeCtrl()->AddCodeSnippet(GetAssociatedItemID(), _("New snippet"), wxEmptyString, 0, true);
  SetFileChanged(true);
}

void CodeSnippetsWindow::ApplySnippet(const wxTreeItemId& itemID) {
  if(const SnippetTreeItemData* item = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(itemID))) {
    if(item->GetType() != SnippetTreeItemData::TYPE_SNIPPET)
      return;
    if(GetConfig()->IsPlugin()) {
      EditorManager* editorMan = Manager::Get()->GetEditorManager();
      if(!editorMan)
        return;
      cbEditor* editor = editorMan->GetBuiltinActiveEditor();
      if(!editor)
        return;
      cbStyledTextCtrl* ctrl = editor->GetControl();
      if(ctrl) {
        wxString snippet = item->GetSnippetString();
        CheckForMacros(snippet);
        snippet.Replace(wxT("\n"), wxT('\n') + editor->GetLineIndentString(ctrl->GetCurrentLine()));
        ctrl->AddText(snippet);
      }
    } else
      AddTextToClipBoard(item->GetSnippetString());
  }
}

void CodeSnippetsWindow::OnMnuApplySnippet(wxCommandEvent&) {
  ApplySnippet(GetAssociatedItemID());
}

wxTreeItemId CodeSnippetsWindow::SearchSnippet(const wxString& searchTerms, const wxTreeItemId& node) {
  wxTreeItemIdValue cookie;
  wxTreeItemId item = GetSnippetsTreeCtrl()->GetFirstChild(node, cookie);
  while(item.IsOk()) {
    if(const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(item))) {
      bool ignoreThisType = false;
      switch(itemData->GetType()) {
        case SnippetTreeItemData::TYPE_ROOT:
          ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_SNIPPET:
          if(GetConfig()->m_SearchConfig.scope == GetConfig()->SCOPE_CATEGORIES)
            ignoreThisType = true;
          break;
        case SnippetTreeItemData::TYPE_CATEGORY:
          if(GetConfig()->m_SearchConfig.scope == GetConfig()->SCOPE_SNIPPETS)
            ignoreThisType = true;
          break;
      }
      if(!ignoreThisType) {
        wxString label = GetSnippetsTreeCtrl()->GetItemText(item);
        if(not GetConfig()->m_SearchConfig.caseSensitive)
          label.LowerCase();
        if(label.Contains(searchTerms))
          return item;
      }
      if(GetSnippetsTreeCtrl()->ItemHasChildren(item)) {
        wxTreeItemId search = SearchSnippet(searchTerms, item);
        if(search.IsOk())
          return search;
      }
      item = GetSnippetsTreeCtrl()->GetNextChild(node, cookie);
    }
  }
  wxTreeItemId dummyItem;
  return dummyItem;
}

void CodeSnippetsWindow::OnBeginLabelEdit(wxTreeEvent& event) {
  if(event.GetItem() == GetSnippetsTreeCtrl()->GetRootItem())
    event.Veto();
  IsEditingLabel(true);
}

void CodeSnippetsWindow::OnMnuLoadSnippetsFromFile(wxCommandEvent& event) {
  wxFileDialog dlg(this, _("Load snippets from file"), wxEmptyString, wxEmptyString,
                   _("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_OPEN);
  if(dlg.ShowModal() == wxID_OK) {
    LOGIT(wxT("LoadingFile:%s"), dlg.GetPath().c_str());
    GetSnippetsTreeCtrl()->LoadItemsFromFile(dlg.GetPath(), m_AppendItemsFromFile);
    GetConfig()->SettingsSnippetsXmlPath = dlg.GetPath();
  }
}

void CodeSnippetsWindow::OnMnuSaveSnippets(wxCommandEvent&) {
  {
    GetSnippetsTreeCtrl()->SaveItemsToFile(GetConfig()->SettingsSnippetsXmlPath);
    SetFileChanged(false);
  }
}

void CodeSnippetsWindow::OnMnuSaveSnippetsAs(wxCommandEvent&) {
  wxFileDialog dlg(this, _("Save snippets to file"), wxEmptyString, _T("codesnippets.xml"), _("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  if(dlg.ShowModal() == wxID_OK) {
    GetSnippetsTreeCtrl()->SaveItemsToFile(dlg.GetPath());
    SetFileChanged(false);
  }
}

void CodeSnippetsWindow::OnMnuFileBackup(wxCommandEvent& event) {
  if(GetFileChanged())
    OnMnuSaveSnippets(event);
  const wxString IndexFile = GetConfig()->SettingsSnippetsXmlPath;
  wxString bkupName = wxEmptyString;
  unsigned i = 0;
  while(true) {
    ++i;
    bkupName = IndexFile;
    bkupName << wxT(".")  << i;
    if(::wxFileExists(bkupName))
      continue;
    break;
  }
  int done = ::wxCopyFile(IndexFile, bkupName);
  GenericMessageBox(wxString::Format(wxT("Backup %s for\n\n %s"),
                                     done ? wxT("succeeded") : wxT("failed"),
                                     bkupName.c_str()));
}

void CodeSnippetsWindow::OnEndLabelEdit(wxTreeEvent& event) {
  IsEditingLabel(false);
  GetSnippetsTreeCtrl()->SortChildren(GetSnippetsTreeCtrl()->GetItemParent(event.GetItem()));
  SetFileChanged(true);
}

void CodeSnippetsWindow::OnMnuRemoveAll(wxCommandEvent&) {
  GetSnippetsTreeCtrl()->DeleteChildren(GetSnippetsTreeCtrl()->GetRootItem());
  SetFileChanged(true);
}

void CodeSnippetsWindow::OnMnuCaseSensitive(wxCommandEvent& event) {
  GetConfig()->m_SearchConfig.caseSensitive = (not GetConfig()->m_SearchConfig.caseSensitive);
}

void CodeSnippetsWindow::OnMnuChangeScope(wxCommandEvent& event) {
  if(event.GetId() == idMnuScopeSnippets)
    GetConfig()->m_SearchConfig.scope = GetConfig()->SCOPE_SNIPPETS;
  else if(event.GetId() == idMnuScopeCategories)
    GetConfig()->m_SearchConfig.scope = GetConfig()->SCOPE_CATEGORIES;
  else if(event.GetId() == idMnuScopeBoth)
    GetConfig()->m_SearchConfig.scope = GetConfig()->SCOPE_BOTH;
}

void CodeSnippetsWindow::OnMnuClear(wxCommandEvent& event) {
  m_SearchSnippetCtrl->Clear();
}

void CodeSnippetsWindow::OnMnuSearchExtended(wxCommandEvent& event) {
  wxWindow* m_pAppWindow = Manager::Get()->GetAppWindow();
  if(not m_pAppWindow)
    m_pAppWindow = wxTheApp->GetTopWindow();
  if(GetFileChanged()) {
    GetSnippetsTreeCtrl()->SaveItemsToFile(GetConfig()->SettingsSnippetsXmlPath);
    #if defined(LOGGING)
    LOGIT(_T("OnMnuSearchExtended Saved[%s]"), GetConfig()->SettingsSnippetsXmlPath.c_str());
    #endif
  }
}

void CodeSnippetsWindow::OnMnuCopyToClipboard(wxCommandEvent& event) {
  if(wxTheClipboard->Open()) {
    const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(GetAssociatedItemID()));
    if(itemData) {
      wxString itemStr = itemData->GetSnippetString();
      static const wxString delim(_T("$%["));
      if(itemStr.find_first_of(delim) != wxString::npos)
        Manager::Get()->GetMacrosManager()->ReplaceMacros(itemStr);
      wxTheClipboard->SetData(new wxTextDataObject(itemStr));
      wxTheClipboard->Close();
    }
  }
}

void CodeSnippetsWindow::OnMnuEditSnippet(wxCommandEvent& WXUNUSED(event)) {
  if(not IsSnippet())
    return;
  CodeSnippetsTreeCtrl* pTree = GetConfig()->GetSnippetsTreeCtrl();
  if(SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(GetAssociatedItemID()))) {
    if(not itemData)
      ;
    wxTreeItemId itemId = GetAssociatedItemID();
    wxString FileName = pTree->GetSnippetFileLink(itemId);
    #if defined(LOGGING)
    LOGIT(_T("OnMnuEditSnippet FileName[%s]"), FileName.c_str());
    #endif
    if(FileName.Length() > 128) {
      GetSnippetsTreeCtrl()->EditSnippetAsText();
      return;
    }
    if((FileName.IsEmpty())
        || (not ::wxFileExists(FileName))) {
      GetSnippetsTreeCtrl()->EditSnippetAsText();
      return;
    }
    GetSnippetsTreeCtrl()->EditSnippetAsFileLink();
  }
}

void CodeSnippetsWindow::OnMnuOpenFileLink(wxCommandEvent& WXUNUSED(event)) {
  if(not IsSnippet())
    return;
  CodeSnippetsTreeCtrl* pTree = GetConfig()->GetSnippetsTreeCtrl();
  if(SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(GetAssociatedItemID()))) {
    if(not itemData)
      ;
    wxTreeItemId itemId = GetAssociatedItemID();
    wxString FileName = pTree->GetSnippetFileLink(itemId);
    LOGIT(_T("OnMnuOpenFileLink FileName[%s]"), FileName.c_str());
    if(FileName.Length() > 128) {
      GetSnippetsTreeCtrl()->EditSnippetAsText();
      return;
    }
    GetSnippetsTreeCtrl()->OpenSnippetAsFileLink();
  }
}

void CodeSnippetsWindow::CheckForMacros(wxString& snippet) {
  wxPoint currentMousePosn = ::wxGetMousePosition();
  int macroPos = snippet.Find(_T("$("));
  int currPosn = macroPos;
  while(macroPos != -1) {
    int macroPosEnd = macroPos + 2;
    int len = (int)snippet.Length();
    while(macroPosEnd < len && snippet.GetChar(macroPosEnd) != _T(')'))
      ++macroPosEnd;
    if(macroPosEnd == len)
      break;
    wxString macroName = snippet.SubString(macroPos + 2, macroPosEnd - 1);
    wxString defaultResult = snippet.SubString(macroPos, macroPosEnd);
    static const wxString delim(_T("$%["));
    if(defaultResult.find_first_of(delim) != wxString::npos)
      Manager::Get()->GetMacrosManager()->ReplaceMacros(defaultResult);
    wxString macro = cbGetTextFromUser(wxString::Format(_("Please enter the text for \"%s\":"), macroName.c_str()),
                                       _("Macro substitution"), defaultResult, 0,
                                       currentMousePosn.x, currentMousePosn.y, false);
    if(not macro.IsEmpty())
      snippet.Replace(_T("$(") + macroName + _T(")"), macro);
    currPosn = currPosn + macroPos + 1;
    macroPos = snippet.Mid(currPosn).Find(_T("$("));
    if(macroPos == -1)
      break;
    macroPos = macroPos + currPosn;
    #if defined(LOGGING)
    #endif
  }
}

void CodeSnippetsWindow::OnItemGetToolTip(wxTreeEvent& event) {
  if(not GetConfig()->GetToolTipsOption())
    return;
  if(const SnippetTreeItemData* itemData = (SnippetTreeItemData*)(GetSnippetsTreeCtrl()->GetItemData(event.GetItem()))) {
    if(itemData->GetType() == SnippetTreeItemData::TYPE_SNIPPET) {
      wxString snippetToolTip = itemData->GetSnippetString();
      size_t originalLength = snippetToolTip.Len();
      snippetToolTip = snippetToolTip.BeforeFirst('\r');
      snippetToolTip = snippetToolTip.BeforeFirst('\n');
      size_t charsInToolTip = 128;
      snippetToolTip = snippetToolTip.Mid(0, charsInToolTip);
      snippetToolTip.Replace(_T("\t"), _T("    "));
      if(not snippetToolTip.IsEmpty())
        if(snippetToolTip.Len() > charsInToolTip || originalLength > charsInToolTip) {
          snippetToolTip = snippetToolTip.Mid(0, charsInToolTip - 4);
          snippetToolTip.Append(_T(" ..."));
        }
      event.SetToolTip(snippetToolTip);
    }
  }
}

bool CodeSnippetsWindow::AddTextToClipBoard(const wxString& text) {
  if(not wxTheClipboard->Open()) {
    wxLogError(GetConfig()->AppName + _T(":Can't open clipboard."));
    return false;
  }
  wxTheClipboard->SetData(new wxTextDataObject(text));
  wxTheClipboard->Close();
  #ifdef LOGGING
  LOGIT(wxT("AddTextToClipBoard()"));
  #endif
  return true;
}

void CodeSnippetsWindow::OnMnuProperties(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  wxTreeItemId itemID = GetAssociatedItemID();
  GetSnippetsTreeCtrl()->EditSnippetProperties(itemID);
}

void CodeSnippetsWindow::OnMnuSettings(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  wxString oldWindowState = GetConfig()->GetSettingsWindowState();
  SettingsDlg* pDlg = new SettingsDlg(this);
  pDlg->ShowModal();
  if(0 != GetConfig()->GetSettingsWindowState().Cmp(oldWindowState))
    GetConfig()->m_bWindowStateChanged = true;
  delete pDlg;
}

void CodeSnippetsWindow::SetSnippetImage(wxTreeItemId itemId) {
  if(IsFileSnippet(itemId))
    GetSnippetsTreeCtrl()->SetItemImage(itemId, TREE_IMAGE_SNIPPET_FILE);
  else
    GetSnippetsTreeCtrl()->SetItemImage(itemId, TREE_IMAGE_SNIPPET_TEXT);
  return;
}

void CodeSnippetsWindow::CheckForExternallyModifiedFiles() {
  if(IsTreeBusy())
    return;
  if(GetFileChanged())
    return;
  wxString whichApp = wxT("CodeSnippets Plugin ");
  if(GetConfig()->IsApplication())
    whichApp = wxT("CodeSnippets Program ");
  if(m_isCheckingForExternallyModifiedFiles)
    return;
  m_isCheckingForExternallyModifiedFiles = true;
  bool failedFiles = false;
  bool b_modified = false;
  if(not wxFileExists(GetConfig()->SettingsSnippetsXmlPath)) {
    m_isCheckingForExternallyModifiedFiles = false;
    return;
  }
  wxFileName fname(GetConfig()->SettingsSnippetsXmlPath);
  wxDateTime last = fname.GetModificationTime();
  #if defined(LOGGING)
  #endif
  wxDateTime fileModTime = GetSnippetsTreeCtrl()->GetSavedFileModificationTime();
  #if defined(LOGGING)
  #endif
  if(fileModTime == time_t(0))
    b_modified = false;
  else if(last.IsLaterThan(GetSnippetsTreeCtrl()->GetSavedFileModificationTime()))
    b_modified = true;
  if(b_modified) {
    int ret = -1;
    {
      wxString msg;
      msg.Printf(_("%s\n\nFile is modified outside the IDE...\nDo you want to reload it (you will lose any unsaved work)?"),
                 GetConfig()->SettingsSnippetsXmlPath.c_str());
      if(GenericMessageBox(msg, whichApp + _("needs to Reload file?!"), wxICON_QUESTION | wxYES_NO) == wxYES)
        ret = wxYES;
      else
        ret = wxNO;
    }
    if(ret == wxYES) {
      if(not GetSnippetsTreeCtrl()->LoadItemsFromFile(GetConfig()->SettingsSnippetsXmlPath, m_AppendItemsFromFile))
        failedFiles = true;
    } else if(ret == wxNO)
      GetSnippetsTreeCtrl()->FetchFileModificationTime();
  }
  if(failedFiles) {
    wxString msg;
    msg.Printf(_("Could not reload file:\n\n%s"), GetConfig()->SettingsSnippetsXmlPath.c_str());
    GenericMessageBox(msg, whichApp +  _("Error"), wxICON_ERROR);
  }
  m_isCheckingForExternallyModifiedFiles = false;
}

void CodeSnippetsWindow::OnMnuEditSnippetAsFileLink(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  GetSnippetsTreeCtrl()->EditSnippetAsFileLink();
}

void CodeSnippetsWindow::OnMnuSaveSnippetAsFileLink(wxCommandEvent& event) {
  SetActiveMenuId(event.GetId());
  GetSnippetsTreeCtrl()->SaveSnippetAsFileLink();
}

void CodeSnippetsWindow::OnIdle(wxIdleEvent& event) {
  event.Skip();
  return;
}

void CodeSnippetsWindow::OnMnuAbout(wxCommandEvent& event) {
  wxString wxbuild(wxVERSION_STRING);
  #if defined(__WXMSW__)
  wxbuild << _T("-Windows");
  #elif defined(__UNIX__)
  wxbuild << _T("-Linux");
  #endif
  #if wxUSE_UNICODE
  wxbuild << _T("-Unicode build");
  #else
  wxbuild << _T("-ANSI build");
  #endif
  wxString buildInfo = wxbuild;
  wxString
  pgmVersionString = wxT("CodeSnippets v") + GetConfig()->GetVersion();
  buildInfo = wxT("\t") + pgmVersionString + wxT("\n") + wxT("\t") + buildInfo;
  buildInfo = buildInfo + wxT("\n\n\t") + wxT("Original Code by Arto Jonsson");
  buildInfo = buildInfo + wxT("\n\t") + wxT("Modified/Enhanced by Pecan Heber");
  ShowSnippetsAbout(buildInfo);
}

void CodeSnippetsWindow::OnMnuTest(wxCommandEvent& event) {
  #if defined(LOGGING)
  LOGIT(_T("Test::EVT_S_DRAGSCROLL_EVENT[%d]"), event.GetId());
  #endif
  wxEvtHandler* ph = GetConfig()->GetDragScrollEvtHandler();
  if(ph) {
    #if defined(LOGGING)
    LOGIT(_T("CodeSnippetWindow Issueing EVT_S_DRAGSCROLL_RESCAN[%s]"), done ? _T("success") : _T("failed"));
    #endif
  }
}

void CodeSnippetsWindow::ShowSnippetsAbout(wxString buildInfo) {
  wxString helpText;
  helpText << wxT("\n\n Each Snippet item may specify either text or a File Link.\n")
           << wxT("\n")
           << wxT(" Snippets may be edited via the context menu \n")
           << wxT("\n")
           << wxT(" File Link snippets are created by dragging text to a new snippet, \n")
           << wxT(" then using the context menu to \"Convert to File Link\". \n")
           << wxT(" The data will be written to the specified file and the filename \n")
           << wxT(" will be placed in the snippets text area as a Link. \n")
           << wxT("\n")
           << wxT(" Snippets are accessed by using the context menu \"Edit\" \n")
           << wxT(" or via the Properties context menu entry. \n")
           << wxT("\n")
           << wxT(" Use the \"Settings\" menu to specify an external editor and \n")
           << wxT(" to specify a non-default Snippets index file. \n")
           << wxT("\n")
           << wxT(" Both the text and file snippets may be dragged outward\n")
           << wxT(" or copied to the clipboard.\n")
           << wxT("\n")
           << wxT(" Dragging a file snippet onto an external program window \n")
           << wxT(" will open the file. Dragging it into the edit area will \n")
           << wxT(" insert the text.\n");
  GenericMessageBox(wxT("\n\n") + buildInfo + helpText, _("About"), wxOK);
}
